<?php

namespace Celebrio\Util;

use \Nette\Object;
use \Nette\Environment;
use ReflectionFunction;
use ReflectionMethod;

/**
 * @author pavel
 */
class AzureUtil extends Object {

    /**
     * Executes given $func with $args as arguments.
     *
     * If an exception is thrown while executing given function AND that exception
     * type was specified in $exceptions parameter, the execution is slept for
     * SLEEP_TIME and it's performed again and again, up to $repeat parameter.
     *
     * If $repeat is not specified and the function always fails, it's executed
     * 5 times and then Exception is thrown. You can also specify desired sleep
     * time, if none is provided, 0.1 sec (100ms) is used. I.e by default, the
     * method will sleep max 0.5 sec.
     *
     * WARNING: $func must be PUBLIC if it's specified as array(class, "method")
     *
     * @static
     * @throws \Exception
     * @param $clazz
     * @param $func
     * @param $object
     * @param array $args
     * @param array $exceptions
     * @param bool $acc_exc_parent
     * @param int $repeat
     * @param int $sleep_time
     * @return mixed
     */
    public static function repeat_action($clazz, $func, $object, array $args = array(), array $exceptions = array(),
        $acc_exc_parent = false, $repeat = 5, $sleep_time = 100) {

        $micro_sleep = $sleep_time * 1000;
        for ($i = 0; $i < $repeat; $i++) {
            try {
                // call user func

                $function = new ReflectionMethod($clazz, $func);
                $function->setAccessible(true);
                return $function->invokeArgs($object, $args);
                //return call_user_func_array($func, $args);
            } catch (\Exception $e) {
                $try_again = false;
                foreach ($exceptions as $exc) {
                    // we'll try again (usleep below) if
                    // a) we accept parent (parameter) and $exc is parent of $e or the $e exception type itself
                    // OR
                    // b) $e exception is exactly of $exc kind
                    if (($acc_exc_parent && ($e instanceof $exc))
                        || (get_class($e) === $exc)
                    ) {
                        $try_again = true;
                        break;
                    }
                }
                if ($try_again) {
                    usleep($micro_sleep);
                } else {
                    // unknown type of exception has been thrown
                    throw $e;
                }
            }

        }

        // the execution failed $repeat times
        throw $e;
    }

    public static $cosik = 0;
}
